package vg.services.user_interface_manager.additional_swing_components;

import java.awt.GridLayout;
import java.util.ArrayDeque;
import java.util.Queue;

import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;

import vg.services.progress_manager.interfaces.IProgressTask;

/**
 * This class need for showing progress.
 * All methods of this class by need called from EDT only!!!
 * @author tzolotuhin
 */
public class ProgressComponent extends JPanel {
	private static final long serialVersionUID = 1L;
	
	private static Updater updater = null;
	
	// Mutex
	private final Object generalMutex = new Object();
	
	// Main components 
	private JLabel progressLabel;
	private IProgressTask task;
	
	public ProgressComponent(IProgressTask task) {
		super(new GridLayout(1, 1));
		this.task = task;
		
		progressLabel = new JLabel("Loading ...");
		progressLabel.setHorizontalAlignment(SwingConstants.CENTER);
		super.add(progressLabel);
		
		if (updater == null) updater = new Updater();
		updater.addProgressComponent(this);		
	}
	
	public void close() {
		synchronized (generalMutex) {
			updater.removeProgressComponent(this);
		}
	} 
	
	public void update() {
		synchronized (generalMutex) {
			if (task != null && task.getLength() <= task.getValue()) {
				updater.removeProgressComponent(this);
			}
			update_view();
		}		
	}

//=============================================================================
//-----------------PRIVATE CLASSES---------------------------------------------
	private void update_view() {
		if (!SwingUtilities.isEventDispatchThread()) {
			// It's bad case, but we need process it
			SwingUtilities.invokeLater(new Runnable() {
				@Override
				public void run() {
					synchronized (generalMutex) {
						update_view();
					}
				}
			});
		} else {
			// It's event dispatch thread
			if (task != null) {
				progressLabel.setText("Loading (" + (100 * task.getValue() / (float)task.getLength()) + "%)");
			} else {
				progressLabel.setText("Loading ...");
			}
		}
	}
//=============================================================================
//-----------------PRIVATE CLASSES---------------------------------------------
	private class Updater implements Runnable {
		// Mutex
		private final Object updaterGeneralMutex = new Object();
		
		// Main components
		private Queue<ProgressComponent> queue;
		
		public Updater() {
			queue = new ArrayDeque<ProgressComponent>();
			Thread t = new Thread(this, "Updater for progress components");
			t.setDaemon(true);
			t.start();
		}
		
		public void addProgressComponent(ProgressComponent comp) {
			synchronized (updaterGeneralMutex) {
				queue.add(comp);
			}
		}
		
		public void removeProgressComponent(ProgressComponent comp) {
			synchronized (updaterGeneralMutex) {
				queue.remove(comp);
			}			
		}
		
		@Override
		public void run() {
			while(true) {
				ProgressComponent[] array = null;
				
				synchronized (updaterGeneralMutex) {
					array = new ProgressComponent[queue.size()];
					array = queue.toArray(array);
				}

				for (ProgressComponent comp : array) {
					comp.update();
				}
				
				try {
					Thread.sleep(500);
				} catch (InterruptedException ex) {
					
				}
			}
		}
	}
}
